import random

from django import forms
from django.shortcuts import render
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from web import models
from django.conf import settings
from utils.tencent.sms import send_sms_single
from django_redis import get_redis_connection
from utils.encrypt import md5
from web.forms.bootstrap import BootStrapForm


class RegisterModelForm(BootStrapForm,forms.ModelForm):
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "密码长度不能小于8个字符",
            'max_length': "密码长度不能大于64个字符"
        },
        widget=forms.PasswordInput()
    )

    # 添加显示字段
    confirm_password = forms.CharField(
        label='重复密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "重复密码长度不能小于8个字符",
            'max_length': "重复密码长度不能大于64个字符"
        },
        widget=forms.PasswordInput())

    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput())

    class Meta:
        model = models.UserInfo
        fields = ['username', 'email', 'password', 'confirm_password', 'mobile_phone', 'code']


    # 用户名校验
    def clean_username(self):
        username = self.cleaned_data['username']
        exists = models.UserInfo.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')
            # 使用 add_error 时，即使用户名已存在但依然会继续执行返回用户名，使用 ValidationError则不会
            # self.add_error('username','用户名已存在')
        return username

    # 邮箱校验
    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('邮箱已存在')
        return email

    # 密码校验
    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return md5(pwd)

    # 确认密码校验
    def clean_confirm_password(self):
        pwd = self.cleaned_data.get('password')
        confirm_pwd = md5(self.cleaned_data['confirm_password'])

        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')

        return confirm_pwd

    # 手机号验证
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('该手机号已注册')
        return mobile_phone

    # 验证码校验
    def clean_code(self):
        code = self.cleaned_data['code']
        # mobile_phone = self.cleaned_data['mobile_phone']
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')

        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')

        return code


class SendSmsForm(forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    # 重定义类，加入参数 request
    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_mobile_phone(self):
        """ 手机号校验的钩子 """
        mobile_phone = self.cleaned_data['mobile_phone']

        # 判断短信模板是否有问题
        tpl = self.request.GET.get('tpl')
        template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not template_id:
            # self.add_error('mobile_phone','短信模板错误')
            raise ValidationError('短信模板错误')

        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if tpl == 'login':
            # 若是登录请求，则判断手机号是否存在
            if not exists:
                raise ValidationError('该手机号没有注册')
        else:
            # 若是注册请求
            # 校验数据库中是否已有手机号
            if exists:
                raise ValidationError('该手机号已注册')

        code = random.randrange(1000, 9999)

        # 发送短信
        sms = send_sms_single(mobile_phone, template_id, [code, ])
        if sms['result'] != 0:
            raise ValidationError("短信发送失败，{}".format(sms['errmsg']))

        # 验证码 写入redis（django-redis）
        conn = get_redis_connection()
        # 有效时间设为 60秒
        conn.set(mobile_phone, code, ex=60)

        return mobile_phone


class LoginSMSForm(BootStrapForm,forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    code = forms.CharField(label="验证码",widget=forms.TextInput())

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        if not mobile_phone:
            raise ValidationError('该手机号没有注册，请注册后再登录')

        return mobile_phone

    def clean_code(self):
        mobile_phone = self.cleaned_data.get('mobile_phone')
        code = self.cleaned_data['code']

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')
        if code.strip() != redis_code.decode('utf-8'):
            raise ValidationError('验证码错误，请重新输入')
        return code



class LoginForm(BootStrapForm,forms.Form):
    username = forms.CharField(label="手机号或邮箱")
    password = forms.CharField(label="密码",min_length=8,max_length=32,widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label="验证码")

    def __init__(self,request,*args,**kwargs):
        super(LoginForm, self).__init__(*args,**kwargs)
        self.request = request

    def clean_password(self):
        password = self.cleaned_data['password']
        return md5(password)

    def clean_code(self):
        code = self.cleaned_data['code']
        session_code =self.request.session.get('img_code')
        if not session_code:
            raise ValidationError("验证码已过期，请重新获取")
        if code.strip().upper() != session_code.upper():
            raise ValidationError("验证码输入错误")
        return code
